/*
 * Copyright 2008 Andrej Herich
 * 
 * http://azetclient.yw.sk/
 * http://azetclient.googlecode.com/
 * 
 * This file is part of AzetClient.
 * 
 * AzetClient is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * AzetClient is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with AzetClient.  If not, see <http://www.gnu.org/licenses/>.
 */

package sk.yw.azetclient.gui;

import java.awt.AWTException;
import java.awt.Dimension;
import java.awt.SystemTray;
import java.awt.Toolkit;
import java.awt.TrayIcon;
import java.util.List;
import javax.swing.ImageIcon;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import org.apache.log4j.Logger;
import sk.yw.azetclient.Main;
import sk.yw.azetclient.gui.actions.AboutAction;
import sk.yw.azetclient.gui.actions.ExitAction;
import sk.yw.azetclient.gui.actions.NextConversationAction;
import sk.yw.azetclient.gui.actions.OpenConversationAction;
import sk.yw.azetclient.gui.actions.PreferencesAction;
import sk.yw.azetclient.managers.BuddyListManager;
import sk.yw.azetclient.managers.ConnectionManager;
import sk.yw.azetclient.managers.ConnectionManagerEvent;
import sk.yw.azetclient.managers.ConnectionManagerListener;
import sk.yw.azetclient.managers.ErrorEvent;
import sk.yw.azetclient.managers.ErrorListener;
import sk.yw.azetclient.managers.MessageThreadManager;
import sk.yw.azetclient.model.Buddy;
import sk.yw.azetclient.model.Message;
import sk.yw.azetclient.model.MessageThread;

/**
 *
 * @author  error216
 */
public class MainFrame extends javax.swing.JFrame
        implements ConnectionManagerListener, ErrorListener,
        ConversationFocusQueueListener {
    
    private static final long serialVersionUID = 349386444l;
    
    private static final Logger logger = Logger.getLogger(MainFrame.class);
    
    private AuthenticationDialog authenticationDialog;
    
    private BuddyListManager buddyListManager;
    private ConnectionManager connectionManager;
    private MessageThreadManager messageThreadManager;
    
    private BuddyListModel buddyListModel = new BuddyListModel();
    private boolean trayIconEnabled = false;
    
    private ConversationManager conversationManager;
    private ConversationFocusQueue conversationFocusQueue =
            new ConversationFocusQueue();
    
    /** Creates new form MainFrame */
    public MainFrame() {
        logger.info("Creating main frame.");
        
        initComponents();
        
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        int x = Math.max(0, screenSize.width - getSize().width);
        int y = Math.max(0, (screenSize.height - getSize().height) / 2);
        setLocation(x, y);
        
        if (SystemTray.isSupported()) {
            logger.info("System tray is supported.");
            try {
                createTrayIcon();
                trayIconEnabled = true;
            } catch (Throwable throwable) {
                logger.error("Unable to create tray icon.", throwable);
            }
        } else {
            logger.info("System tray is not supported.");
        }
        
        conversationFocusQueue.addConversationFocusQueueListener(this);
        
        logger.info("Main frame created.");
    }
    
    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        jScrollPane1 = new javax.swing.JScrollPane();
        buddyList = new javax.swing.JList();
        jMenuBar1 = new javax.swing.JMenuBar();
        programMenu = new javax.swing.JMenu();
        requestNextFocusItem = new javax.swing.JMenuItem();
        openConversationItem = new javax.swing.JMenuItem();
        preferencesItem = new javax.swing.JMenuItem();
        exitItem = new javax.swing.JMenuItem();
        helpMenu = new javax.swing.JMenu();
        jMenuItem1 = new javax.swing.JMenuItem();

        setDefaultCloseOperation(javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE);
        setTitle(Main.APPLICATION_NAME + " " + Main.VERSION);
        setIconImage((new ImageIcon(this.getClass().getClassLoader().getResource("sk/yw/azetclient/img/icon.png"))).getImage());
        addWindowListener(new java.awt.event.WindowAdapter() {
            public void windowClosing(java.awt.event.WindowEvent evt) {
                MainFrame.this.windowClosing(evt);
            }
        });

        buddyList.setModel(buddyListModel);
        buddyList.setCellRenderer(new BuddyCellRenderer());
        buddyList.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                buddyListMouseClicked(evt);
            }
        });
        jScrollPane1.setViewportView(buddyList);

        programMenu.setText(Main.getText("menu.program"));

        requestNextFocusItem.setAction(new NextConversationAction(conversationFocusQueue));
        programMenu.add(requestNextFocusItem);
        programMenu.add(openConversationItem);

        preferencesItem.setAction(new PreferencesAction());
        programMenu.add(preferencesItem);

        exitItem.setAction(new ExitAction());
        programMenu.add(exitItem);

        jMenuBar1.add(programMenu);

        helpMenu.setText(Main.getText("menu.help"));

        jMenuItem1.setAction(new AboutAction());
        helpMenu.add(jMenuItem1);

        jMenuBar1.add(helpMenu);

        setJMenuBar(jMenuBar1);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 270, Short.MAX_VALUE)
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jScrollPane1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 588, Short.MAX_VALUE)
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents

    private void windowClosing(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_windowClosing
        if (trayIconEnabled) {
            setVisible(false);
            repaint();
        } else {
            (new ExitAction()).actionPerformed(null);
        }
}//GEN-LAST:event_windowClosing

    private void buddyListMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_buddyListMouseClicked
        if (evt.getSource().equals(buddyList) && evt.getClickCount() == 2) {
            Buddy buddy = (Buddy) buddyList.getSelectedValue();
            MessageThread thread = createNewThread();
            conversationManager.createConversationPanel(buddy, thread, true);
        }
    }//GEN-LAST:event_buddyListMouseClicked
    
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JList buddyList;
    private javax.swing.JMenuItem exitItem;
    private javax.swing.JMenu helpMenu;
    private javax.swing.JMenuBar jMenuBar1;
    private javax.swing.JMenuItem jMenuItem1;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JMenuItem openConversationItem;
    private javax.swing.JMenuItem preferencesItem;
    private javax.swing.JMenu programMenu;
    private javax.swing.JMenuItem requestNextFocusItem;
    // End of variables declaration//GEN-END:variables
    
    public void start() {
        if (!"true".equals(
                Main.getProperty("azetclient.license.agreed"))) {
            (new LicenseDialog(this)).setVisible(true);
        }
        if ("true".equals(
                Main.getProperty("azetclient.automaticAuthentication"))) {
            authenticate(Main.getProperty("azetclient.username"),
                    Main.getProperty("azetclient.password"));
        } else {
            authenticationDialog = new AuthenticationDialog(this, true);
            authenticationDialog.setVisible(true);
        }
    }
    
    private void createTrayIcon() throws AWTException {
        final TrayMenu trayMenu = new TrayMenu(this);
        final TrayIcon trayIcon = new TrayIcon(getIconImage());
        final SystemTray tray = SystemTray.getSystemTray();
        
        trayIcon.addActionListener(trayMenu.getChangeMainFrameVisibility());
        
        trayIcon.setImageAutoSize(true);
        trayIcon.setPopupMenu(trayMenu);
        tray.add(trayIcon);
    }
    
    public void authenticate(String username, final String password) {
        final Buddy user = new Buddy();
        user.setName(username);
        
        buddyListManager = new BuddyListManager();
        
        messageThreadManager = new MessageThreadManager(user);
        messageThreadManager.addErrorListener(this);
        
        connectionManager = new ConnectionManager(
                buddyListManager, messageThreadManager);
        connectionManager.addConnectionManagerListener(this);
        connectionManager.addErrorListener(this);
        
        conversationManager = new ConversationManager(user);
        messageThreadManager.addMessageThreadManagerListener(
                conversationManager);
        
        (new Thread() {
            @Override
            public void run() {
                connectionManager.authenticate(user, password);
            }
        }).start();
    }
    
    public void stop() {
        if (connectionManager != null) {
            connectionManager.cancelAll();
            connectionManager.disconnect();
        }
        
        if (messageThreadManager != null) {
            messageThreadManager.cancelAll();
            messageThreadManager.storeThreads();
        }
    }

    @Override
    public void errorOccured(ErrorEvent event) {
        final javax.swing.JFrame frame = this;
        final Throwable error = event.getThrowable();
        final StringBuffer errorMessage = new StringBuffer(error.toString());
        for (StackTraceElement element : error.getStackTrace()) {
            errorMessage.append('\n');
            errorMessage.append(element.toString());
        }
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                JOptionPane.showMessageDialog(frame,
                        errorMessage.toString(),
                        Main.getText("error"),
                        JOptionPane.ERROR_MESSAGE);
            }
        });
    }

    @Override
    public void authenticationSucceeded(ConnectionManagerEvent event) {
        buddyListModel.initialAdd(buddyListManager.getBuddies());
        buddyListManager.addBuddyListManagerListener(buddyListModel);
        
        final Buddy user = (Buddy) event.getSubject();
        final javax.swing.JFrame frame = this;
        SwingUtilities.invokeLater(new Runnable(){
            @Override
            public void run() {
                openConversationItem.setAction(
                        new OpenConversationAction(user, null, frame));
                if (authenticationDialog != null) {
                    authenticationDialog.dispose();
                }
            }
        });
    }

    @Override
    public void authenticationFailed(ConnectionManagerEvent event) {
        final javax.swing.JFrame frame = this;
        if (authenticationDialog == null) {
            SwingUtilities.invokeLater(new Runnable(){
                @Override
                public void run() {
                    authenticationDialog =
                            new AuthenticationDialog(frame, true);
                    authenticationDialog.setFieldsEnabled(false);
                    authenticationDialog.setVisible(true);
                }
            });
        }
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                JOptionPane.showMessageDialog(authenticationDialog,
                        Main.getText("authenticationFailed.message"),
                        Main.getText("authenticationFailed.title"),
                        JOptionPane.WARNING_MESSAGE);
                authenticationDialog.setFieldsEnabled(true);
            }
        });
        
    }

    @Override
    public void messageNotSent(ConnectionManagerEvent event) {
        
    }

    public ConversationFocusQueue getConversationFocusQueue() {
        return conversationFocusQueue;
    }
    
    public void sendMessage(Message message) {
        connectionManager.sendMessage(message);
    }

    @Override
    public void queueIsEmpty() {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                setTitle(Main.APPLICATION_NAME + " " + Main.VERSION);
            }
        });
    }

    @Override
    public void queueIsNotEmpty() {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                setTitle(Main.getProperty(
                        "azetclient.mainFrame.title.newMessage") + " "
                        + Main.APPLICATION_NAME + " " + Main.VERSION);
            }
        });
    }
    
    public MessageThread createNewThread() {
        return messageThreadManager.createNewThread();
    }
    
    public MessageThread openThread(MessageThread thread) {
        return messageThreadManager.openThread(thread);
    }
    
    public List<MessageThread> getThreads(Buddy buddy) {
        return messageThreadManager.getThreads(buddy);
    }
    
    public void closeThread(MessageThread thread) {
        messageThreadManager.closeThread(thread);
    }
    
    public Buddy getBuddy(String name) {
        return buddyListManager.getBuddy(name);
    }
    
    public Buddy getOther(String name) {
        return buddyListManager.getOther(name);
    }
}
